package service

import (
	"context"
	"fmt"
	"strconv"
	"time"
	"unicode/utf8"

	"github.com/sirupsen/logrus"

	"wespy-http-go/app/activity/2025/summer_drink_car/conf"
	"wespy-http-go/app/activity/2025/summer_drink_car/store"
	"wespy-http-go/app/activity/common/acterror"
	"wespy-http-go/app/activity/common/actinfo"
	"wespy-http-go/app/activity/common/actlogger"
	"wespy-http-go/app/activity/common/actmsg"
	"wespy-http-go/app/activity/common/actreward"
	"wespy-http-go/app/activity/common/actstore"
	"wespy-http-go/app/activity/common/actutil"
	collectChipService "wespy-http-go/app/activity/widget/collect_chip/service"
	rankService "wespy-http-go/app/activity/widget/rank/service"
	rankStore "wespy-http-go/app/activity/widget/rank/store"
	"wespy-http-go/def"
	"wespy-http-go/helper"
	"wespy-http-go/pkg/safego"
	"wespy-http-go/pkg/weconfig/wenamespace/weactivity"
)

// ChangeName 修改饮品车名称
func ChangeName(c context.Context, req *store.ChangeNameReq) (*store.ChangeNameRsp, error) {
	entry := actlogger.GetLogger(c)
	actInfo := actinfo.GetValidActByUid(conf.ActId, req.Uid)
	if actInfo == nil {
		return nil, acterror.NewCustomError(actmsg.ActTimeErr)
	}
	actConfig := conf.GetActConfig(actInfo)
	if actConfig == nil {
		return nil, acterror.NewCustomError(actmsg.ConfigErr)
	}

	// 检查名称长度
	if utf8.RuneCountInString(req.Name) > actConfig.NameLength {
		return nil, acterror.NewWithMsg(def.CodeError, actConfig.Messages.NameToLongMsg)
	}

	// 脏词检查
	checkResp := helper.CheckDirtyWordV3(int32(req.Uid), 0, req.Name, def.DirtyFilterNickname, helper.YiDunTextThirdPart)
	if checkResp.HasRiskOrPoliticReview() {
		entry.Warnf("change drink car name(%s) fail, contains dirty word", req.Name)
		return nil, acterror.NewCustomError(actmsg.DirtyWordErr)
	}

	// 保存店名
	err := store.SetDrinkCarName(req.ActId, req.Uid, req.Name)
	if err != nil {
		entry.WithError(err).Errorln("summer_drink_car ChangeName SetDrinkCarName err")
		return nil, err
	}

	entry.WithField("name", req.Name).Infoln("summer_drink_car ChangeName success")
	return &store.ChangeNameRsp{}, nil
}

// OpenDoor 开门营业
func OpenDoor(c context.Context, req *store.OpenDoorReq) (*store.OpenDoorRsp, error) {
	entry := actlogger.GetLogger(c)
	actInfo := actinfo.GetValidActByUid(conf.ActId, req.Uid)
	if actInfo == nil {
		return nil, acterror.NewCustomError(actmsg.ActTimeErr)
	}
	actConfig := conf.GetActConfig(actInfo)
	if actConfig == nil {
		return nil, acterror.NewCustomError(actmsg.ConfigErr)
	}
	now := actInfo.GetActCurrentTime()
	day := now.Format(def.TimeFormatDayNoSep)

	lockKey := store.GetUserLockKey(req.ActId, req.Uid)
	lockRes := actstore.TryLockOnce(lockKey, 2)
	if lockRes.Failed() {
		return nil, acterror.NewCustomError(actmsg.LockErr)
	}
	defer lockRes.Unlock()

	state, err := store.GetOpenState(req.ActId, req.Uid, day)
	if err != nil {
		return nil, err
	}
	if state != conf.StateNotOpen {
		return nil, acterror.NewWithMsg(def.CodeError, "正在营业中")
	}
	err = store.SetOpenState(req.ActId, req.Uid, day, conf.StateOpen)
	if err != nil {
		return nil, err
	}
	err = NextFreeFlyer(actConfig, req.Uid, now, now)
	if err != nil {
		entry.WithError(err).Errorln("summer_drink_car OpenDoor nextFreeFlyer err")
	}
	entry.Infoln("summer_drink_car OpenDoor success")
	return &store.OpenDoorRsp{}, nil
}

// NextFreeFlyer 设置下次免费传单时间
func NextFreeFlyer(config *conf.ActConfig, uid int, now time.Time, lastTime time.Time) error {
	if now.Day() != lastTime.Day() {
		return nil
	}
	dayEndTime := time.Date(now.Year(), now.Month(), now.Day(), 0, 0, 0, 0, now.Location()).AddDate(0, 0, 1)
	dayEndUnix := int(dayEndTime.Unix())
	nextUnix := int(now.Add(time.Duration(config.FreeFlyerDuration) * time.Second).Unix())
	if nextUnix < dayEndUnix+config.LastFlyerDuration {
		nowUnix := int(now.Unix())
		// 保证不跨天
		deferNextTime := nextUnix
		if nextUnix >= dayEndUnix {
			deferNextTime = dayEndUnix - 1 // 每天最后一秒
		}
		deferTime := deferNextTime - nowUnix
		if deferTime <= 0 {
			deferTime = 0
		}
		actutil.CreateDeferTask(conf.ActId, conf.DeferTaskFreeFlyer, deferTime, &store.FreeFlyerTask{
			Uid:       uid,
			Timestamp: deferNextTime,
		})
	}
	err := store.SetNextFlyerTime(conf.ActId, uid, now.Format(def.TimeFormatDayNoSep), nextUnix)
	if err != nil {
		return err
	}
	return nil
}

// SignIn 签到
func SignIn(c context.Context, req *store.SignInReq) (*store.SignInRsp, error) {
	entry := actlogger.GetLogger(c).WithField("type", req.Type)
	rsp := &store.SignInRsp{}
	actInfo := actinfo.GetValidActByUid(conf.ActId, req.Uid)
	if actInfo == nil {
		return nil, acterror.NewCustomError(actmsg.ActTimeErr)
	}
	config := conf.GetActConfig(actInfo)
	if config == nil {
		return nil, acterror.NewCustomError(actmsg.ConfigErr)
	}
	now := actInfo.GetActCurrentTime()
	day := req.Day
	if day != now.Format(def.TimeFormatDayNoSep) {
		return nil, acterror.NewWithMsg(def.CodeError, config.Messages.ExpiredMsg)
	}
	dayConfig := config.GetSignDayConfig(day)
	if dayConfig == nil {
		return nil, acterror.NewCustomError(actmsg.OperateFailErr)
	}
	isUnlock := isUnlockAdvanced(conf.ActId, req.Uid)

	lockKey := store.GetSignLockKey(conf.ActId, req.Uid)
	lockRes := actstore.TryLockOnce(lockKey, 2)
	if lockRes.Failed() {
		return nil, acterror.NewCustomError(actmsg.LockErr)
	}
	defer lockRes.Unlock()

	normalStateMap, err := store.GetNormalStateMap(conf.ActId, req.Uid)
	if err != nil {
		return nil, err
	}
	advancedStateMap, err := store.GetAdvancedStateMap(conf.ActId, req.Uid)
	if err != nil {
		return nil, err
	}
	normalState, advancedState := getSignState(dayConfig, day, normalStateMap[day], advancedStateMap[day], isUnlock)
	var rewards []*actreward.RewardInfo
	switch req.Type {
	case conf.SignTypeNormal:
		if normalState != conf.SignStateComplete {
			return nil, acterror.NewCustomError(actmsg.OperateFailErr)
		}
		rewards = actreward.GenerateRewards(req.Uid, 0, dayConfig.NormalReward)
		err = store.SetNormalState(conf.ActId, req.Uid, day, 1)
	case conf.SignTypeAdvanced:
		if advancedState != conf.SignStateComplete {
			return nil, acterror.NewCustomError(actmsg.OperateFailErr)
		}
		rewards = actreward.GenerateRewards(req.Uid, 0, dayConfig.AdvancedReward)
		err = store.SetAdvancedState(conf.ActId, req.Uid, day, 1)
	}
	if err != nil {
		entry.WithError(err).Errorln("summer_drink_car SignIn SetState err")
		return nil, err
	}
	err = actreward.SendRewards(req.Uid, rewards, &actreward.ExtraInfo{
		ActId:      req.ActId,
		ActName:    actInfo.Name,
		ChipSource: "签到",
	})
	if err != nil {
		entry.WithError(err).Errorln("summer_drink_car SignIn SendRewards err")
	}
	rsp.Rewards = rewards
	return rsp, nil
}

// GetSignDetails 获取签到详情
func GetSignDetails(c context.Context, req *store.GetSignDetailsReq) (*store.GetSignDetailsRsp, error) {
	rsp := &store.GetSignDetailsRsp{
		List: make([]*store.SignDetail, 0),
	}
	actInfo := actinfo.GetValidActByUid(conf.ActId, req.Uid)
	if actInfo == nil {
		return nil, acterror.NewCustomError(actmsg.ActTimeErr)
	}
	config := conf.GetActConfig(actInfo)
	if config == nil {
		return nil, acterror.NewCustomError(actmsg.ConfigErr)
	}
	now := actInfo.GetActCurrentTime()
	day := now.Format(def.FormatTimeDayNoSeparator)

	normalStateMap, err := store.GetNormalStateMap(conf.ActId, req.Uid)
	if err != nil {
		return nil, err
	}
	advancedStateMap, err := store.GetAdvancedStateMap(conf.ActId, req.Uid)
	if err != nil {
		return nil, err
	}
	isUnlock := isUnlockAdvanced(conf.ActId, req.Uid)
	for _, v := range config.SignConfig.List {
		normalState, advancedState := getSignState(v, day, normalStateMap[v.Day], advancedStateMap[v.Day], isUnlock)
		rsp.List = append(rsp.List, &store.SignDetail{
			Day:           v.Day,
			NormalState:   normalState,
			AdvancedState: advancedState,
		})
	}
	return rsp, nil
}

// GetSignPopup 获取签到弹窗
func GetSignPopup(c context.Context, req *store.GetSignPopupReq) (*store.GetSignPopupRsp, error) {
	rsp := &store.GetSignPopupRsp{
		Rewards: make([]*actreward.RewardInfo, 0),
	}
	actInfo := actinfo.GetValidActByUid(conf.ActId, req.Uid)
	if actInfo == nil {
		return nil, acterror.NewCustomError(actmsg.ActTimeErr)
	}
	config := conf.GetActConfig(actInfo)
	if config == nil {
		return nil, acterror.NewCustomError(actmsg.ConfigErr)
	}

	lockKey := store.GetSignLockKey(conf.ActId, req.Uid)
	lockRes := actstore.TryLockOnce(lockKey, 2)
	if lockRes.Failed() {
		return nil, acterror.NewCustomError(actmsg.LockErr)
	}
	defer lockRes.Unlock()

	rewards, err := store.GetSignPopup(req.ActId, req.Uid)
	if err != nil {
		return nil, err
	}
	if len(rewards) > 0 {
		err = store.ClearSignPopup(req.ActId, req.Uid)
		if err != nil {
			return nil, err
		}
	}
	rsp.Rewards = rewards
	return rsp, nil
}

// GetShopDetails 获取店铺详情
func GetShopDetails(c context.Context, req *store.GetShopDetailsReq) (*store.GetShopDetailsRsp, error) {
	entry := actlogger.GetLogger(c)
	rsp := &store.GetShopDetailsRsp{}
	actInfo := actinfo.GetValidActByUid(conf.ActId, req.Uid)
	if actInfo == nil {
		return nil, acterror.NewCustomError(actmsg.ActTimeErr)
	}
	actConfig := conf.GetActConfig(actInfo)
	if actConfig == nil {
		return nil, acterror.NewCustomError(actmsg.ConfigErr)
	}
	now := actInfo.GetActCurrentTime()
	day := now.Format(def.FormatTimeDayNoSeparator)

	// 获取店铺名称
	name, err := store.GetDrinkCarName(conf.ActId, req.Uid)
	if err != nil {
		entry.WithError(err).Errorln("summer_drink_car GetShopDetails GetDrinkCarName err")
		return nil, err
	}

	// 获取营业状态
	openState, err := store.GetOpenState(conf.ActId, req.Uid, day)
	if err != nil {
		entry.WithError(err).Errorln("summer_drink_car GetShopDetails GetOpenState err")
		return nil, err
	}

	// 检查是否解锁高级功能
	unlockAdvanced := isUnlockAdvanced(conf.ActId, req.Uid)

	// 获取下次传单时间
	nextFlyerTime, err := store.GetNextFlyerTime(conf.ActId, req.Uid, day)
	if err != nil {
		entry.WithError(err).Errorln("summer_drink_car GetShopDetails GetNextFlyerTime err")
		return nil, err
	}

	// 将时间戳转换为日期格式
	var nextFlyerTimeStr string
	if nextFlyerTime > 0 {
		nextFlyerTimeStr = time.Unix(int64(nextFlyerTime), 0).Format(def.FormatTimeWithSeparator)
	} else {
		nextFlyerTimeStr = ""
	}

	// 检查是否能领取小费
	checkoutInfo, err := store.GetCheckoutInfo(conf.ActId, req.Uid)
	if err != nil {
		entry.WithError(err).Errorln("summer_drink_car GetShopDetails GetCheckoutInfo err")
		return nil, err
	}
	tipNum := checkoutInfo[store.CheckoutFieldTipNum]
	reputationScore := checkoutInfo[store.CheckoutFieldReputationScore]
	isGet := tipNum > 0 || reputationScore > 0

	rankConfig := GetRankConfig(actInfo, 1) // 口碑排行榜
	userRank := 0
	if rankConfig != nil {
		rank, err := rankStore.GetUserRank(actInfo, rankConfig, now, strconv.Itoa(req.Uid))
		if err != nil {
			entry.WithError(err).Warnln("summer_drink_car GetShopDetails GetUserRank err")
		} else {
			userRank = rank
		}
	}

	// 计算当前排行榜区间结束时间（格式化为字符串，参考next_flyer_time的处理）
	var currentRankEndTimeStr string
	if actConfig.RankStartHour > 0 && actConfig.RankDurationHour > 0 {
		currentHour := now.Hour()
		var rankHour int

		if currentHour < actConfig.RankStartHour {
			// 跨天的情况：0:00-12:00之间，属于22:00-0:00时段
			rankHour = 22
		} else {
			// 计算当前是第几轮排行榜
			rankRound := (currentHour - actConfig.RankStartHour) / actConfig.RankDurationHour
			rankHour = actConfig.RankStartHour + rankRound*actConfig.RankDurationHour
		}

		// 计算区间结束时间
		endHour := rankHour + actConfig.RankDurationHour
		var nextTime time.Time
		if endHour >= 24 {
			// 跨天的情况：23:00-0:00，结束时间是明天0:00
			nextTime = time.Date(now.Year(), now.Month(), now.Day()+1, 0, 0, 0, 0, now.Location())
		} else {
			nextTime = time.Date(now.Year(), now.Month(), now.Day(), endHour, 0, 0, 0, now.Location())
		}

		// 格式化时间戳为字符串（和next_flyer_time一样的处理）
		currentRankEndTimeStr = nextTime.Format(def.FormatTimeWithSeparator)
	} else {
		currentRankEndTimeStr = ""
	}

	rsp.Name = name
	rsp.OpenDoor = openState > 0
	rsp.Advanced = unlockAdvanced
	rsp.NextFlyerTime = nextFlyerTimeStr
	rsp.IsGet = isGet
	rsp.Rank = userRank
	rsp.CurrentRankEndTime = currentRankEndTimeStr

	// 参与活动标志 - 异步发送
	safego.SafeGo(func() {
		extra := &actreward.ExtraInfo{
			ActId:   conf.ActId,
			ActName: actInfo.Name,
		}
		reward := &actreward.RewardInfo{
			ActId:      conf.ActId,
			RewardType: actreward.RewardTypeCollectChip,
			RewardId:   conf.ChipIdJoinFlag,
			RewardVal:  1,
		}
		_ = actreward.SendRewards(req.Uid, []*actreward.RewardInfo{reward}, extra)
	})

	entry.WithField("name", name).WithField("open_door", rsp.OpenDoor).WithField("advanced", rsp.Advanced).Infoln("summer_drink_car GetShopDetails success")
	return rsp, nil
}

// OpenReceiveRewards 解锁高级功能奖励
func OpenReceiveRewards(entry interface{}, config interface{}, uid int) error {
	actlogEntry, ok := entry.(*logrus.Entry)
	if !ok {
		return nil
	}
	actConfig, ok := config.(*conf.ActConfig)
	if !ok {
		return nil
	}

	actInfo := actConfig.Activity
	now := actInfo.GetActCurrentTime()
	day := now.Format(def.FormatTimeDayNoSeparator)
	actlogEntry = actlogEntry.WithField("day", day)
	isUnlock := isUnlockAdvanced(conf.ActId, uid)

	lockKey := store.GetSignLockKey(conf.ActId, uid)
	lockRes := actstore.TryLockWithRetry(lockKey, 5)
	if lockRes.Failed() {
		return acterror.NewCustomError(actmsg.LockErr)
	}
	defer lockRes.Unlock()

	advancedStateMap, err := store.GetAdvancedStateMap(conf.ActId, uid)
	if err != nil {
		return err
	}
	dayList := make([]string, 0)
	rewards := make([]*actreward.RewardInfo, 0)
	for _, v := range actConfig.SignConfig.List {
		actlogEntry = actlogEntry.WithField("config_day", v.Day)
		_, advancedState := getSignState(v, day, 0, advancedStateMap[v.Day], isUnlock)
		if !helper.InArrayInt(advancedState, []int{conf.SignStateComplete, conf.SignStateExpired}) {
			continue
		}
		err = store.SetAdvancedState(conf.ActId, uid, v.Day, 1)
		if err != nil {
			actlogEntry.WithError(err).Errorln("summer_drink_car OpenReceiveRewards set advanced state err")
			continue
		}
		dayList = append(dayList, v.Day)
		rewards = append(rewards, actreward.GenerateRewards(uid, 0, v.AdvancedReward)...)
	}
	actlogEntry = actlogEntry.WithField("day_list", dayList)
	if len(rewards) == 0 {
		actlogEntry.Infoln("summer_drink_car OpenReceiveRewards no rewards")
		return nil
	}
	err = actreward.SendRewards(uid, rewards, &actreward.ExtraInfo{
		ActId:      conf.ActId,
		ChipSource: "开通秘典",
	})
	if err != nil {
		actlogEntry.WithError(err).Errorln("summer_drink_car OpenReceiveRewards send rewards err")
		return err
	}
	mergeRewards := actreward.MergeRewards(uid, rewards)
	err = store.SetSignPopup(conf.ActId, uid, mergeRewards)
	if err != nil {
		actlogEntry.WithError(err).Errorln("summer_drink_car OpenReceiveRewards set popup err")
		return err
	}
	actlogEntry.Infoln("summer_drink_car OpenReceiveRewards success")
	return nil
}

// getSignState 获取签到状态
func getSignState(dayConfig *conf.DayConfig, day string, normalState, advancedState int, isUnlockAdvanced bool) (int, int) {
	if dayConfig.Day > day {
		return conf.SignStateInit, conf.SignStateInit
	}
	normal, advanced := conf.SignStateComplete, conf.SignStateComplete
	if !isUnlockAdvanced {
		advanced = conf.SignStateInit
	}
	if dayConfig.Day < day {
		normal, advanced = conf.SignStateExpired, conf.SignStateExpired
	}
	if normalState > 0 {
		normal = conf.SignStateReceived
	}
	if advancedState > 0 {
		advanced = conf.SignStateReceived
	}
	return normal, advanced
}

// BuyAdvancedBook 购买高级营业秘籍
func BuyAdvancedBook(c context.Context, req *actutil.CommonReq) error {
	entry := actlogger.GetLogger(c)

	// 检查是否已经购买过
	if isUnlockAdvanced(conf.ActId, req.Uid) {
		return acterror.NewWithMsg(def.CodeError, "您已经购买过高级营业秘籍了")
	}

	// 发放购买标志碎片，这会触发HandleCollectChip事件
	buyFlagReward := &actreward.RewardInfo{
		ActId:      conf.ActId,
		RewardType: actreward.RewardTypeCollectChip,
		RewardId:   conf.ChipIdBuyFlag,
		RewardVal:  1,
	}
	err := actreward.SendRewards(req.Uid, []*actreward.RewardInfo{buyFlagReward}, &actreward.ExtraInfo{
		ActId:      req.ActId,
		ChipSource: "购买高级营业秘籍",
	})
	if err != nil {
		entry.WithError(err).Errorln("summer_drink_car BuyAdvancedBook send buy flag err")
		return err
	}

	entry.Infoln("summer_drink_car BuyAdvancedBook success")
	return nil
}

// GetRankList 获取排行榜列表
func GetRankList(c context.Context, req *store.GetRankListReq) (*store.GetRankListRsp, error) {
	entry := actlogger.GetLogger(c)
	rsp := &store.GetRankListRsp{}

	// 验证活动有效性
	actInfo := actinfo.GetValidActByUid(conf.ActId, req.Uid)
	if actInfo == nil {
		return nil, acterror.NewCustomError(actmsg.ActTimeErr)
	}

	// 获取活动配置
	actConfig := conf.GetActConfig(actInfo)
	if actConfig == nil {
		return nil, acterror.NewCustomError(actmsg.ConfigErr)
	}

	// 获取排行榜配置
	rankConfig := GetRankConfig(actInfo, req.RankId)
	if rankConfig == nil {
		entry.WithField("rank_id", req.RankId).Errorln("summer_drink_car GetRankList rank config not found")
		return nil, acterror.NewCustomError(actmsg.ConfigErr)
	}

	now := actInfo.GetActCurrentTime()

	// 直接使用当前时间进行排行榜查询，Hook会自动处理时间分段逻辑
	displayTime := now

	// 设置默认值
	if req.Limit <= 0 {
		req.Limit = 10 // 默认获取前10名
	}
	if req.Start < 0 {
		req.Start = 0
	}

	// 获取用户排名和积分 - 使用显示时间
	memberStr := strconv.Itoa(req.Uid)
	userRank, err := rankStore.GetUserRank(actInfo, rankConfig, displayTime, memberStr)
	if err != nil {
		entry.WithError(err).Errorln("summer_drink_car GetRankList GetUserRank err")
		// 排名获取失败不影响整体接口，设置默认值
		rsp.MyRank = 0
	} else {
		rsp.MyRank = userRank
	}

	// 获取用户积分 - 使用显示时间
	userScore, err := rankStore.GetUserRankScore(actInfo, rankConfig, displayTime, memberStr)
	if err != nil {
		entry.WithError(err).Errorln("summer_drink_car GetRankList GetUserRankScore err")
		// 积分获取失败不影响整体接口，设置默认值
		rsp.MyScore = 0
	} else {
		rsp.MyScore = userScore
	}

	// 获取排行榜列表 - 使用显示时间
	members, scores, err := rankStore.GetRankList(actInfo, rankConfig, displayTime, req.Start, req.Limit)
	if err != nil {
		entry.WithError(err).Errorln("summer_drink_car GetRankList GetRankList err")
		return nil, err
	}

	// 构建排行榜列表数据
	rsp.RankList = make([]*store.RankUserInfo, 0, len(members))

	// 批量获取用户信息
	uidList := make([]int, 0, len(members))
	for _, member := range members {
		uid, err := strconv.Atoi(member)
		if err != nil {
			entry.WithError(err).WithField("member", member).Warnln("summer_drink_car GetRankList invalid member")
			continue
		}
		uidList = append(uidList, uid)
	}

	// 批量获取用户信息
	userInfoMap, err := actutil.GetActUserInfoMap(uidList)
	if err != nil {
		entry.WithError(err).Warnln("summer_drink_car GetRankList GetActUserInfoMap err")
		// 即使获取用户信息失败，也继续返回排行榜数据，只是没有头像和昵称
		userInfoMap = make(map[int]actutil.ActUserInfo)
	}

	for i, member := range members {
		uid, err := strconv.Atoi(member)
		if err != nil {
			entry.WithError(err).WithField("member", member).Warnln("summer_drink_car GetRankList invalid member")
			continue
		}

		// 将字符串分数转换为整数
		score, err := strconv.Atoi(scores[i])
		if err != nil {
			entry.WithError(err).WithField("score", scores[i]).Warnln("summer_drink_car GetRankList invalid score")
			score = 0
		}

		userInfo := &store.RankUserInfo{
			Uid:   uid,
			Score: score,
			Rank:  req.Start + i + 1, // 排名从1开始
		}

		// 填充用户基本信息（头像、昵称）
		if actUserInfo, exists := userInfoMap[uid]; exists {
			userInfo.Avatar = actUserInfo.HeadImgUrl
			userInfo.Name = actUserInfo.Nickname
		}

		rsp.RankList = append(rsp.RankList, userInfo)
	}

	// 计算排行榜结束时间和时间段描述
	rsp.RankEndTime = calculateRankEndTimeStr(actConfig, now)
	rsp.RankPeriod = calculateRankPeriod(actConfig, now)

	entry.WithField("my_rank", rsp.MyRank).
		WithField("my_score", rsp.MyScore).
		WithField("list_count", len(rsp.RankList)).
		WithField("rank_period", rsp.RankPeriod).
		Infoln("summer_drink_car GetRankList success")

	return rsp, nil
}

// GetRankConfig 获取排行榜配置
func GetRankConfig(actInfo *weactivity.Activity, rankId int) *weactivity.Rank {
	// 从活动配置中查找对应的排行榜配置
	for _, rank := range actInfo.Rank {
		if rank.Type == strconv.Itoa(rankId) {
			return rank
		}
	}
	return nil
}

// calculateRankEndTime 计算排行榜结束时间
func calculateRankEndTime(actConfig *conf.ActConfig, now time.Time) (int, int) {
	// 如果没有配置排行榜时间，返回0
	if actConfig.RankStartHour == 0 || actConfig.RankDurationHour == 0 {
		return 0, 0
	}

	currentHour := now.Hour()

	// 计算当前排行榜时间段
	var currentRankEndTime, nextRankEndTime time.Time

	if currentHour < actConfig.RankStartHour {
		// 当前时间在排行榜开始之前
		currentRankEndTime = time.Date(now.Year(), now.Month(), now.Day(),
			actConfig.RankStartHour+actConfig.RankDurationHour, 0, 0, 0, now.Location())
		nextRankEndTime = currentRankEndTime.Add(time.Duration(actConfig.RankDurationHour) * time.Hour)
	} else {
		// 计算当前是第几轮排行榜
		rankRound := (currentHour - actConfig.RankStartHour) / actConfig.RankDurationHour
		currentRankStartHour := actConfig.RankStartHour + rankRound*actConfig.RankDurationHour

		currentRankEndTime = time.Date(now.Year(), now.Month(), now.Day(),
			currentRankStartHour+actConfig.RankDurationHour, 0, 0, 0, now.Location())
		nextRankEndTime = currentRankEndTime.Add(time.Duration(actConfig.RankDurationHour) * time.Hour)
	}

	return int(currentRankEndTime.Unix()), int(nextRankEndTime.Unix())
}

// calculateRankEndTimeStr 计算当前排行榜区间结束时间（格式化为字符串）
func calculateRankEndTimeStr(actConfig *conf.ActConfig, now time.Time) string {
	// 如果没有配置排行榜时间，返回空字符串
	if actConfig.RankStartHour == 0 || actConfig.RankDurationHour == 0 {
		return ""
	}

	currentHour := now.Hour()
	var rankHour int

	if currentHour < actConfig.RankStartHour {
		// 跨天的情况：0:00-12:00之间，属于22:00-0:00时段
		rankHour = 22
	} else {
		// 计算当前是第几轮排行榜
		rankRound := (currentHour - actConfig.RankStartHour) / actConfig.RankDurationHour
		rankHour = actConfig.RankStartHour + rankRound*actConfig.RankDurationHour
	}

	// 计算区间结束时间
	endHour := rankHour + actConfig.RankDurationHour
	var nextTime time.Time
	if endHour >= 24 {
		// 跨天的情况：22:00-0:00，结束时间是明天0:00
		nextTime = time.Date(now.Year(), now.Month(), now.Day()+1, 0, 0, 0, 0, now.Location())
	} else {
		nextTime = time.Date(now.Year(), now.Month(), now.Day(), endHour, 0, 0, 0, now.Location())
	}

	// 格式化时间戳为字符串（和next_flyer_time一样的处理）
	return nextTime.Format(def.FormatTimeWithSeparator)
}

// calculateRankPeriod 计算当前排行榜时间段描述
func calculateRankPeriod(actConfig *conf.ActConfig, now time.Time) string {
	if actConfig.RankStartHour == 0 || actConfig.RankDurationHour == 0 {
		return "未配置"
	}

	currentHour := now.Hour()

	if currentHour < actConfig.RankStartHour {
		// 跨天的情况：0:00-12:00之间，属于23:00-0:00时段
		return "23:00-0:00"
	} else {
		// 计算当前是第几轮排行榜
		rankRound := (currentHour - actConfig.RankStartHour) / actConfig.RankDurationHour
		currentRankStartHour := actConfig.RankStartHour + rankRound*actConfig.RankDurationHour
		currentRankEndHour := currentRankStartHour + actConfig.RankDurationHour

		// 处理跨天的情况
		if currentRankEndHour >= 24 {
			return "23:00-0:00"
		}

		return fmt.Sprintf("%d:00-%d:00", currentRankStartHour, currentRankEndHour)
	}
}

// TestHourlyRank 测试小时排行榜功能
func TestHourlyRank(c context.Context, req *store.TestHourlyRankReq) (*store.TestHourlyRankRsp, error) {
	entry := actlogger.GetLogger(c)
	rsp := &store.TestHourlyRankRsp{}

	// 验证活动有效性
	actInfo := actinfo.GetValidActByUid(conf.ActId, req.Uid)
	if actInfo == nil {
		return nil, acterror.NewCustomError(actmsg.ActTimeErr)
	}

	// 获取活动配置
	actConfig := conf.GetActConfig(actInfo)
	if actConfig == nil {
		return nil, acterror.NewCustomError(actmsg.ConfigErr)
	}

	// 获取排行榜配置
	rankConfig := GetRankConfig(actInfo, 1) // rankId=1，口碑排行榜
	if rankConfig == nil {
		return nil, acterror.NewCustomError(actmsg.ConfigErr)
	}

	now := actInfo.GetActCurrentTime()
	member := strconv.Itoa(req.Uid)
	score := req.Score
	if score <= 0 {
		score = 10 // 默认积分
	}

	// 填充当前状态信息
	rsp.CurrentHour = now.Hour()
	rsp.Score = score

	// 检查时间有效性
	if actConfig.RankStartHour > 0 && actConfig.RankDurationHour > 0 {
		rsp.IsValidTime = isRankTimeValidForTest(actConfig, now)
		rsp.RankHour = getCurrentRankHourForTest(actConfig, now)
	} else {
		rsp.IsValidTime = true
		rsp.RankHour = now.Hour()
	}

	// 尝试增加积分，看看Hook是否生效
	totalScore, err := rankService.IncrRankValueWithTime(actInfo, rankConfig.Type, rankConfig.PeriodType, member, score, now)
	if err != nil {
		entry.WithError(err).Errorln("summer_drink_car TestHourlyRank IncrRankValueWithTime err")
		return nil, err
	}

	rsp.TotalScore = totalScore
	rsp.HookTriggered = totalScore > 0 // 如果积分增加了，说明Hook允许了

	// 获取实际使用的Redis Key（通过排行榜组件）
	redisKey, err := rankStore.GetRedisKey(actInfo, rankConfig, now)
	if err != nil {
		entry.WithError(err).Errorln("summer_drink_car TestHourlyRank GetRedisKey err")
	} else {
		rsp.RedisKey = redisKey
	}

	// 获取时间格式
	timeFormat, err := rankStore.GetTimeFormat(actInfo, rankConfig, now)
	if err != nil {
		entry.WithError(err).Errorln("summer_drink_car TestHourlyRank GetTimeFormat err")
	} else {
		rsp.TimeFormat = timeFormat
	}

	entry.WithField("redis_key", rsp.RedisKey).
		WithField("time_format", rsp.TimeFormat).
		WithField("total_score", rsp.TotalScore).
		WithField("hook_triggered", rsp.HookTriggered).
		Infoln("summer_drink_car TestHourlyRank success")

	return rsp, nil
}

// isRankTimeValidForTest 测试用的时间有效性检查
func isRankTimeValidForTest(actConfig *conf.ActConfig, now time.Time) bool {
	if actConfig.RankStartHour == 0 {
		return true
	}

	currentHour := now.Hour()
	if currentHour >= actConfig.RankStartHour {
		rankRound := (currentHour - actConfig.RankStartHour) / actConfig.RankDurationHour
		rankStartHour := actConfig.RankStartHour + rankRound*actConfig.RankDurationHour
		rankEndHour := rankStartHour + actConfig.RankDurationHour
		return currentHour >= rankStartHour && currentHour < rankEndHour
	}
	return false
}

// getCurrentRankHourForTest 测试用的获取当前排行榜时间段
func getCurrentRankHourForTest(actConfig *conf.ActConfig, now time.Time) int {
	if actConfig.RankStartHour == 0 || actConfig.RankDurationHour == 0 {
		return now.Hour()
	}

	currentHour := now.Hour()
	if currentHour < actConfig.RankStartHour {
		return actConfig.RankStartHour
	}

	rankRound := (currentHour - actConfig.RankStartHour) / actConfig.RankDurationHour
	return actConfig.RankStartHour + rankRound*actConfig.RankDurationHour
}

// TestHourlyCheckout 测试小时排行榜结算功能
func TestHourlyCheckout(c context.Context, req *store.TestHourlyCheckoutReq) (*store.TestHourlyCheckoutRsp, error) {
	entry := actlogger.GetLogger(c)
	rsp := &store.TestHourlyCheckoutRsp{}

	// 验证活动有效性
	actInfo := actinfo.GetValidActByUid(conf.ActId, req.Uid)
	if actInfo == nil {
		return nil, acterror.NewCustomError(actmsg.ActTimeErr)
	}

	// 获取活动配置
	actConfig := conf.GetActConfig(actInfo)
	if actConfig == nil {
		return nil, acterror.NewCustomError(actmsg.ConfigErr)
	}

	// 验证小时参数
	if (req.Hour < actConfig.RankStartHour+actConfig.RankDurationHour || req.Hour > 23) && req.Hour != 0 {
		return nil, acterror.NewCustomError(actmsg.ParamsErr)
	}

	// 计算上一个时间段和时间段显示
	var prevHour int
	var period string

	if req.Hour == 0 {
		// 特殊处理：0点结算23:00-0:00时段
		prevHour = 23
		period = "23:00-0:00"
	} else {
		prevHour = req.Hour - actConfig.RankDurationHour
		period = fmt.Sprintf("%d:00-%d:00", prevHour, req.Hour)
	}

	// 获取排行榜配置
	rankConfig := GetRankConfig(actInfo, 1) // rankId=1，口碑排行榜
	if rankConfig == nil {
		return nil, acterror.NewCustomError(actmsg.ConfigErr)
	}

	// 构造上一个时间段的时间
	now := actInfo.GetActCurrentTime()
	prevTime := time.Date(now.Year(), now.Month(), now.Day(), prevHour, 0, 0, 0, now.Location())

	// 获取前三名
	top3Members, _, err := rankStore.GetRankList(actInfo, rankConfig, prevTime, 0, 3)
	if err != nil {
		entry.WithError(err).Errorln("summer_drink_car TestHourlyCheckout GetRankList err")
		return nil, err
	}

	// 处理前三名，发送4号碎片奖励
	winners := make([]int, 0, 3)
	for i, member := range top3Members {
		if i >= 3 {
			break
		}

		uid, err := strconv.Atoi(member)
		if err != nil {
			entry.WithError(err).WithField("member", member).
				Errorln("summer_drink_car TestHourlyCheckout parse uid err")
			continue
		}

		// 发送4号碎片（宝箱钥匙）给优秀店长
		result, err := collectChipService.AddUserChip(conf.ActId, uid, conf.ChipIdBoxKey, 1, &collectChipService.AddCollectChipExtra{
			Source: fmt.Sprintf("小时排行榜结算奖励-%s", period),
		})

		if err != nil {
			entry.WithError(err).WithField("uid", uid).WithField("period", period).
				WithField("chip_id", conf.ChipIdBoxKey).
				Errorln("summer_drink_car TestHourlyCheckout 发送碎片失败")
		} else {
			entry.WithField("uid", uid).WithField("rank", i+1).
				WithField("period", period).WithField("reward", "宝箱钥匙x1").WithField("new_total", result).
				Infoln("summer_drink_car TestHourlyCheckout 优秀店长产生，奖励已发放")
		}

		winners = append(winners, uid)
	}

	rsp.ProcessedHour = req.Hour
	rsp.Period = period
	rsp.Top3Count = len(top3Members)
	rsp.Winners = winners
	rsp.Message = fmt.Sprintf("成功处理%s时段的排行榜结算，产生%d位优秀店长", period, len(winners))

	entry.WithField("period", period).
		WithField("top3_count", len(top3Members)).
		WithField("winners", winners).
		Infoln("summer_drink_car TestHourlyCheckout success")

	return rsp, nil
}
